#include "degfx/driver.h"

#ifdef MX__DRIVER_DJGPP

#   define MXMODULE_DRS

#   include <pc.h>
#   include <dpmi.h>
#   include <go32.h>
#   include <conio.h>
#   include <sys/farptr.h>
#   include <string.h>

extern unsigned mx__djgpp_mouse_valid;
extern unsigned mx__djgpp_pointer(void);
extern unsigned mx__djgpp_key(int *scan, int *ascii);

/* General information */
static const char mx__djgpp_vesa_title[] = "vesa";
static unsigned int mx__djgpp_vesa_scanline;
static void (*mx__djgpp_vesa_pixelline) (const int x1, const int y1, const int x2, const MX_PIXEL * color);

/* Linear mode variables */
static unsigned mx__djgpp_vesa_linearmode;
static short mx__djgpp_vesa_video_ds;
static __dpmi_meminfo mx__djgpp_vesa_meminfo;

/* Bank switching */
static unsigned int mx__djgpp_vesa_bank;
static void (*mx__djgpp_vesa_bank_switch) (unsigned int bank_number) = 0;

typedef struct MX__DJGPP_VESA_PM_INFO {
	unsigned short setWindow __attribute__ ((packed));
	unsigned short setDisplayStart __attribute__ ((packed));
	unsigned short setPalette __attribute__ ((packed));
	unsigned short IOPrivInfo __attribute__ ((packed));
} MX__DJGPP_VESA_PM_INFO;

static MX__DJGPP_VESA_PM_INFO *mx__djgpp_vesa_pm_info = 0;
static unsigned mx__djgpp_vesa_pm_info_size = 0;
static void *mx__djgpp_vesa_pm_bank_switcher = 0;

typedef struct MX__DJGPP_VESA_INFO {
	unsigned char VESASignature[4] __attribute__ ((packed));
	unsigned short VESAVersion __attribute__ ((packed));
	unsigned long OEMStringPtr __attribute__ ((packed));
	unsigned char Capabilities[4] __attribute__ ((packed));
	unsigned long VideoModePtr __attribute__ ((packed));
	unsigned short TotalMemory __attribute__ ((packed));
	unsigned short OemSoftwareRev __attribute__ ((packed));
	unsigned long OemVendorNamePtr __attribute__ ((packed));
	unsigned long OemProductNamePtr __attribute__ ((packed));
	unsigned long OemProductRevPtr __attribute__ ((packed));
	unsigned char Reserved[222] __attribute__ ((packed));
	unsigned char OemData[256] __attribute__ ((packed));
} MX__DJGPP_VESA_INFO;

static int mx__djgpp_vesa_info(MX__DJGPP_VESA_INFO * vesa_info)
{
	__dpmi_regs r;
	unsigned int c;

	/* Clear out the transfer buffer to start */
	const long dosbuf = __tb & 0xFFFFF;

	for (c = 0; c < sizeof(MX__DJGPP_VESA_INFO); c++)
		_farpokeb(_dos_ds, dosbuf + c, 0);

	dosmemput("VBE2", 4, dosbuf);

	/* Get the vesa info to the transfer buffer */
	r.x.ax = 0x4F00;
	r.x.di = dosbuf & 0xF;
	r.x.es = (dosbuf >> 4) & 0xFFFF;
	__dpmi_int(0x10, &r);
	if (r.h.ah)
		return -1;

	/* Copy the transfer data to our memory space */
	dosmemget(dosbuf, sizeof(MX__DJGPP_VESA_INFO), vesa_info);

	if (strncmp((const char *) vesa_info->VESASignature, "VESA", 4) != 0)
		return -1;

	return 0;
}

typedef struct MODE_INFO {
	unsigned short ModeAttributes __attribute__ ((packed));
	unsigned char WinAAttributes __attribute__ ((packed));
	unsigned char WinBAttributes __attribute__ ((packed));
	unsigned short WinGranularity __attribute__ ((packed));
	unsigned short WinSize __attribute__ ((packed));
	unsigned short WinASegment __attribute__ ((packed));
	unsigned short WinBSegment __attribute__ ((packed));
	unsigned long WinFuncPtr __attribute__ ((packed));
	unsigned short BytesPerScanLine __attribute__ ((packed));
	unsigned short XResolution __attribute__ ((packed));
	unsigned short YResolution __attribute__ ((packed));
	unsigned char XCharSize __attribute__ ((packed));
	unsigned char YCharSize __attribute__ ((packed));
	unsigned char NumberOfPlanes __attribute__ ((packed));
	unsigned char BitsPerPixel __attribute__ ((packed));
	unsigned char NumberOfBanks __attribute__ ((packed));
	unsigned char MemoryModel __attribute__ ((packed));
	unsigned char BankSize __attribute__ ((packed));
	unsigned char NumberOfImagePages __attribute__ ((packed));
	unsigned char Reserved_page __attribute__ ((packed));
	unsigned char RedMaskSize __attribute__ ((packed));
	unsigned char RedMaskPos __attribute__ ((packed));
	unsigned char GreenMaskSize __attribute__ ((packed));
	unsigned char GreenMaskPos __attribute__ ((packed));
	unsigned char BlueMaskSize __attribute__ ((packed));
	unsigned char BlueMaskPos __attribute__ ((packed));
	unsigned char ReservedMaskSize __attribute__ ((packed));
	unsigned char ReservedMaskPos __attribute__ ((packed));
	unsigned char DirectColorModeInfo __attribute__ ((packed));
	unsigned long PhysBasePtr __attribute__ ((packed));
	unsigned long OffScreenMemOffset __attribute__ ((packed));
	unsigned short OffScreenMemSize __attribute__ ((packed));
	unsigned char Reserved[206] __attribute__ ((packed));
} MODE_INFO;

static int mx__djgpp_vesa_modeinfo(int mode, MODE_INFO * mode_info)
{
	__dpmi_regs r;
	unsigned int c;

	/* First, zero the transfer buffer */
	const long dosbuf = __tb & 0xFFFFF;

	for (c = 0; c < sizeof(MODE_INFO); c++)
		_farpokeb(_dos_ds, dosbuf + c, 0);

	/* Get the mode info to the transfer buffer */
	r.x.ax = 0x4F01;
	r.x.di = dosbuf & 0xF;
	r.x.es = (dosbuf >> 4) & 0xFFFF;
	r.x.cx = mode;
	__dpmi_int(0x10, &r);

	/* Mode info did not work */
	if (r.h.ah)
		return -1;

	/* Copy the buffer to our variable */
	dosmemget(dosbuf, sizeof(MODE_INFO), mode_info);
	return 0;
}

static int mx__djgpp_vesa_scoremode(MODE_INFO * mode, int x, int y, int c)
{
	/* Error for resolution differences */
	const int xdiff = x - mode->XResolution;
	const int ydiff = y - mode->YResolution;
	const int cdiff = c - mode->BitsPerPixel;
	int err = (xdiff * xdiff) + (ydiff * ydiff) + (cdiff * cdiff);

	/* Supported in hardware and graphic mode */
	if ((mode->ModeAttributes & 0x19) != 0x19)
		return 0x0fffffff;

	/* We prefer a linear mode if possible */
	if (mode->ModeAttributes & 0x80)
		--err;

	/* We can only handle certian memory model */
	switch (mode->MemoryModel) {

		/* Packed pixel is ok, with some restrictions */
	case 4:
		if ((mode->WinGranularity != 64) || (mode->WinSize != 64)
			|| (mode->BitsPerPixel != 8))
			return 0x0fffffff;
		break;

		/* We prefer direct color if possible */
	case 6:
		--err;
		break;

		/* We cant handle other wierd modes */
	case 0:					/* text */
	case 1:					/* CGA */
	case 2:					/* Hercules */
	case 3:					/* planar */
	case 5:					/* non-chain 4, 256 color */
	case 7:					/* YUV */
	default:
		return 0x0fffffff;
	}
	return err;
}

static int get_vesa_pm_functions(const MX__DJGPP_VESA_INFO * vesa)
{
	__dpmi_regs r;

	/* check that the driver is at least VBE version 2.0 */
	if (vesa->VESAVersion < 0x200)
		return -1;

	/* call the VESA function */
	r.x.ax = 0x4F0A;
	r.x.bx = 0;
	__dpmi_int(0x10, &r);
	if (r.h.ah)
		return -1;

	/* allocate space for the code stubs */
	mx__djgpp_vesa_pm_info = malloc(r.x.cx);
	_go32_dpmi_lock_data(mx__djgpp_vesa_pm_info, r.x.cx);
	mx__djgpp_vesa_pm_info_size = r.x.cx;

	/* copy the code into our address space */
	dosmemget(r.x.es * 16 + r.x.di, r.x.cx, mx__djgpp_vesa_pm_info);

	/* store a pointer to the bank switch routine */
	mx__djgpp_vesa_pm_bank_switcher = (void *) ((char *) mx__djgpp_vesa_pm_info + mx__djgpp_vesa_pm_info->setWindow);
	return 0;
}

static int mx__djgpp_vesa_findmode(MODE_INFO * ret, int w, int h, int c)
{
	int i;
	int mode_list[1024];
	int number_of_modes;
	long mode_ptr;
	int bestmode = 0;
	int bestscore = 0x0fffffff;

	MX__DJGPP_VESA_INFO vesa;

	/* Get the basic VESA information */
	if (mx__djgpp_vesa_info(&vesa) != 0)
		return 0;

	/* Check for protected mode interface */
	get_vesa_pm_functions(&vesa);

	/* Make a list of avialable modes */
	mode_ptr = ((vesa.VideoModePtr & 0xFFFF0000) >> 12) + (vesa.VideoModePtr & 0xFFFF);
	number_of_modes = 0;

	while (_farpeekw(_dos_ds, mode_ptr) != 0xFFFF) {
		mode_list[number_of_modes] = _farpeekw(_dos_ds, mode_ptr);
		number_of_modes++;
		mode_ptr += 2;
	}

	/* Check out if the mode matches what we want */
	for (i = 0; i < number_of_modes; i++) {
		int score;
		MODE_INFO mode;

		if (mx__djgpp_vesa_modeinfo(mode_list[i], &mode) != 0)
			continue;

		score = mx__djgpp_vesa_scoremode(&mode, w, h, c);

		/* Is it a closely matching resolution */
		if (score < bestscore) {
			bestmode = mode_list[i];
			bestscore = score;
			*ret = mode;
		}
	}
	return bestmode;
}

static void mx__djgpp_vesa_setbank(unsigned int bank_number)
{
	__dpmi_regs r;

	r.x.ax = 0x4F05;
	r.x.bx = 0;
	r.x.dx = bank_number;
	__dpmi_int(0x10, &r);

	mx__djgpp_vesa_bank = bank_number;
}

static void mx__djgpp_vesa_pm_setbank(unsigned int bank_number)
{
  asm(" call *%0 ":			/* no outputs */
  :"r"(mx__djgpp_vesa_pm_bank_switcher),

/* function pointer in any register */
"b"(0),							/* set %ebx to zero */
"d"(bank_number)				/* bank number in %edx */
  :"%eax",						/* clobber list (we have no way of */
/* "%ebx", *//* knowing which registers the VESA */
"%ecx",							/* code is going to change, so we */
/* "%edx", *//* have to assume the worst and list */
"%esi"							/* them all here) */
/* "%edi" */ );
	mx__djgpp_vesa_bank = bank_number;
}

static const MX_PIXEL *mx__djgpp_copy332(unsigned int address, const unsigned int endaddress, const MX_PIXEL * color)
{
	while (address <= endaddress) {
		_farnspokeb(address++, MXRGB332(*color));
		++color;
	}

	return color;
}

#   define MX__DJGPP_VESA_BANKVAR(factor)                           \
     const unsigned int y1scanline = y1 * mx__djgpp_vesa_scanline;     \
     unsigned int address = y1scanline + (x1 * factor);    \
     unsigned int endaddress = y1scanline + (x2 * factor); \
     const unsigned int bank = address >> 16;              \
     const unsigned int endbank = endaddress >> 16;        \
     address = 0xa0000 + (address & 0xffff);               \
     endaddress = 0xa0000 + (endaddress & 0xffff)

static void mx__djgpp_vesa_banked8(const int x1, const int y1, const int x2, const MX_PIXEL * color)
{
	MX__DJGPP_VESA_BANKVAR(1);

	if (bank != mx__djgpp_vesa_bank)
		mx__djgpp_vesa_bank_switch(bank);

	/* Start and end on the same bank */
	if (bank == endbank) {
		mx__djgpp_copy332(address, endaddress, color);

		/* Start and end on different banks */
	} else {
		color = mx__djgpp_copy332(address, 0xaffff, color);
		mx__djgpp_vesa_bank_switch(endbank);
		mx__djgpp_copy332(0xa0000, endaddress, color);
	}
}

static void mx__djgpp_vesa_banked15(const int x1, const int y1, const int x2, const MX_PIXEL * color)
{
	MX__DJGPP_VESA_BANKVAR(2);

	if (bank != mx__djgpp_vesa_bank)
		mx__djgpp_vesa_bank_switch(bank);

	/* Start and end on the same bank */
	if (bank == endbank) {

		while (address <= endaddress) {
			_farnspokew(address, MXRGB555(*color));
			++color;
			address += 2;
		}

		/* Start and end on different banks */
	} else {
		while (address <= 0xaffff) {
			_farnspokew(address += 2, MXRGB555(*color));
			++color;
		}

		address = 0xa0000;
		mx__djgpp_vesa_bank_switch(endbank);

		while (address <= endaddress) {
			_farnspokew(address += 2, MXRGB555(*color));
			++color;
		}
	}
}

static void mx__djgpp_vesa_banked16(const int x1, const int y1, const int x2, const MX_PIXEL * color)
{
	MX__DJGPP_VESA_BANKVAR(2);

	if (bank != mx__djgpp_vesa_bank)
		mx__djgpp_vesa_bank_switch(bank);

	/* Start and end on the same bank */
	if (bank == endbank) {

		while (address <= endaddress) {
			_farnspokew(address, MXRGB565(*color));
			++color;
			address += 2;
		}

		/* Start and end on different banks */
	} else {
		while (address <= 0xaffff) {
			_farnspokew(address, MXRGB565(*color));
			++color;
			address += 2;
		}

		address = 0xa0000;
		mx__djgpp_vesa_bank_switch(endbank);

		while (address <= endaddress) {
			_farnspokew(address, MXRGB565(*color));
			++color;
			address += 2;
		}
	}
}

static void mx__djgpp_vesa_banked24(const int x1, const int y1, const int x2, const MX_PIXEL * color)
{
	MX__DJGPP_VESA_BANKVAR(3);

	if (bank != mx__djgpp_vesa_bank)
		mx__djgpp_vesa_bank_switch(bank);

	/* Start and end on the same bank */
	if (bank == endbank) {

		while (address <= endaddress) {
			_farnspokeb(address++, MXR(*color));
			_farnspokeb(address++, MXG(*color));
			_farnspokeb(address++, MXB(*color));
			++color;
		}

		/* Start and end on different banks */
	} else {
		while (address <= 0xaffff) {
			_farnspokeb(address++, MXR(*color));
			_farnspokeb(address++, MXG(*color));
			_farnspokeb(address++, MXB(*color));
			++color;
		}

		address = 0xa0000;
		mx__djgpp_vesa_bank_switch(endbank);

		while (address <= endaddress) {
			_farnspokeb(address++, MXR(*color));
			_farnspokeb(address++, MXG(*color));
			_farnspokeb(address++, MXB(*color));
			++color;
		}
	}
}

static void mx__djgpp_vesa_banked32(const int x1, const int y1, const int x2, const MX_PIXEL * color)
{
	MX__DJGPP_VESA_BANKVAR(4);

	if (bank != mx__djgpp_vesa_bank)
		mx__djgpp_vesa_bank_switch(bank);

	/* Start and end on the same bank */
	if (bank == endbank) {
		while (address <= endaddress) {
			_farnspokel(address, MXRGB888(*color));
			++color;
			address += 4;
		}

		/* Start and end on different banks */
	} else {
		while (address <= 0xaffff) {
			_farnspokel(address, MXRGB888(*color));
			++color;
			address += 4;
		}

		address = 0xa0000;
		mx__djgpp_vesa_bank_switch(endbank);

		while (address <= endaddress) {
			_farnspokel(address, MXRGB888(*color));
			++color;
			address += 4;
		}
	}
}

#   define MX__DJGPP_VESA_LINEARVAR(factor)                         \
    const unsigned int y1scanline = y1 * mx__djgpp_vesa_scanline;    \
    const unsigned int end = y1scanline + (x2 * factor); \
    unsigned int start = y1scanline + (x1 * factor);     \


static void mx__djgpp_vesa_linear8(const int x1, const int y1, const int x2, const MX_PIXEL * color)
{
	MX__DJGPP_VESA_LINEARVAR(1);
	mx__djgpp_copy332(start, end, color);
}

static void mx__djgpp_vesa_linear15(const int x1, const int y1, const int x2, const MX_PIXEL * color)
{
	MX__DJGPP_VESA_LINEARVAR(2);

	while (start <= end) {
		_farnspokew(start, MXRGB555(*color));
		++color;
		start += 2;
	}
}

static void mx__djgpp_vesa_linear16(const int x1, const int y1, const int x2, const MX_PIXEL * color)
{
	MX__DJGPP_VESA_LINEARVAR(2);

	while (start <= end) {
		_farnspokew(start, MXRGB565(*color));
		++color;
		start += 2;
	}
}

static void mx__djgpp_vesa_linear24(const int x1, const int y1, const int x2, const MX_PIXEL * color)
{
	MX__DJGPP_VESA_LINEARVAR(3);

	while (start <= end) {
		_farnspokeb(start++, MXR(*color));
		_farnspokeb(start++, MXG(*color));
		_farnspokeb(start++, MXB(*color));
		++color;
	}
}

static void mx__djgpp_vesa_linear32(const int x1, const int y1, const int x2, const MX_PIXEL * color)
{
	MX__DJGPP_VESA_LINEARVAR(4);

	while (start <= end) {
		_farnspokel(start, MXRGB888(*color));
		++color;
		start += 4;
	}
}

static unsigned mx__djgpp_vesa_try(const int mode)
{
	__dpmi_regs r;

	/* Set the graphics mode */
	r.x.ax = 0x4F02;
	r.x.bx = mode;
	__dpmi_int(0x10, &r);

	/* Check for failure */
	if ((r.h.al != 0x4f) || (r.h.ah))
		return false;

	return true;
}

static void mx__djgpp_vesa_palette(void)
{
	unsigned i;

	outportb(0x3C8, 0);

	for (i = 0; i <= 255; i++) {
		unsigned char r = (i & 0xe0) + (i >> 3);
		unsigned char g = ((i & 0x1c) << 3) + (i >> 3);
		unsigned char b = ((i & 0x07) << 6) + (i >> 2);

		outportb(0x3C9, r >> 2);
		outportb(0x3C9, g >> 2);
		outportb(0x3C9, b >> 2);
	}
}

static unsigned mx__djgpp_vesa_start(MX_GFX_ARGS * args)
{
	MODE_INFO mode;
	int mode_number;
	__dpmi_regs r;

	memset(&mode, 0, sizeof(mode));

	/* Set defaults */
	if ((args->w == 0) || (args->w == MXDEFAULT))
		args->w = 640;
	if ((args->h == 0) || (args->w == MXDEFAULT))
		args->h = 480;
	if ((args->c == 0) || (args->w == MXDEFAULT))
		args->c = 16;

	/* We can only handle some depths modes */
	if (args->c > 32)
		args->c = 32;
	if (args->c < 8)
		args->c = 8;

	/* Find the mode with the right resolution */
	mode_number = mx__djgpp_vesa_findmode(&mode, args->w, args->h, args->c);
	if (mode_number == 0)
		return 0;

	/* Try to set the linear mode if we can */
	mx__djgpp_vesa_linearmode = (mode.ModeAttributes & 0x80) ? true : false;

	/* Try to set the mode */
	if (mx__djgpp_vesa_linearmode) {

		/* Get physical mapping for linear mode */
		mx__djgpp_vesa_meminfo.size = mode.XResolution * mode.BytesPerScanLine;
		mx__djgpp_vesa_meminfo.address = mode.PhysBasePtr;
		if (__dpmi_physical_address_mapping(&mx__djgpp_vesa_meminfo) == -1) {
			mx__djgpp_vesa_linearmode = false;
			goto lineardone;
		}

		/* Actually set the mode */
		mx__djgpp_vesa_linearmode = mx__djgpp_vesa_try(mode_number | 0x4000);
	}

	/* Linear model appeard to work, so try to set the descriptor and limits */
	if (mx__djgpp_vesa_linearmode) {

		/* Mapping worked, try to get descriptor for video memory */
		mx__djgpp_vesa_video_ds = __dpmi_allocate_ldt_descriptors(1);
		if (mx__djgpp_vesa_video_ds == -1) {

			/* Could not get descriptor, so release mapping too */
			__dpmi_free_physical_address_mapping(&mx__djgpp_vesa_meminfo);
			mx__djgpp_vesa_linearmode = false;
			goto lineardone;
		}

		/* Set the base to the video memory */
		if (__dpmi_set_segment_base_address(mx__djgpp_vesa_video_ds, mx__djgpp_vesa_meminfo.address) == -1) {

			/* Could not set selector base, so release everything */
			__dpmi_free_ldt_descriptor(mx__djgpp_vesa_video_ds);
			__dpmi_free_physical_address_mapping(&mx__djgpp_vesa_meminfo);
			mx__djgpp_vesa_linearmode = false;
			goto lineardone;
		}

		/* We got descriptor, make it the right size */
		if (__dpmi_set_segment_limit(mx__djgpp_vesa_video_ds, mx__djgpp_vesa_meminfo.size | 0xffff) == -1) {

			/* Descriptor size failed, so release everything */
			__dpmi_free_ldt_descriptor(mx__djgpp_vesa_video_ds);
			__dpmi_free_physical_address_mapping(&mx__djgpp_vesa_meminfo);
			mx__djgpp_vesa_linearmode = false;
			goto lineardone;
		}
	}
  lineardone:

	/* Linear mode didnt work, try banked mode */
	if (!mx__djgpp_vesa_linearmode) {
		if (!mx__djgpp_vesa_try(mode_number & ~0x4000)) {

			/* Banked mode failed too, so fail */
			return false;
		}

	}

	/* Set the right bank switch routine */
	if ((mx__djgpp_vesa_pm_info) && (mx__djgpp_vesa_pm_bank_switcher))
		mx__djgpp_vesa_bank_switch = mx__djgpp_vesa_pm_setbank;
	else
		mx__djgpp_vesa_bank_switch = mx__djgpp_vesa_setbank;

	/* Start off with some specific vesa bank */
	mx__djgpp_vesa_bank_switch(0);

	/* Setup the right color mixing functions */
	mx__djgpp_vesa_scanline = mode.BytesPerScanLine;
	if (mode.BitsPerPixel == 8) {
		mx__djgpp_vesa_pixelline = mx__djgpp_vesa_linearmode ? mx__djgpp_vesa_linear8 : mx__djgpp_vesa_banked8;

		/* Setup 332 palette for 8 bit modes */
		mx__djgpp_vesa_palette();

		/* For 15/16 bit modes */
	} else if (mode.BitsPerPixel == 16) {

		/* For 555 modes */
		if ((mode.RedMaskSize == 5) && (mode.RedMaskPos == 10)
			&& (mode.GreenMaskSize == 5) && (mode.GreenMaskPos == 5)
			&& (mode.BlueMaskSize == 5) && (mode.BlueMaskPos == 0)) {
			mx__djgpp_vesa_pixelline = mx__djgpp_vesa_linearmode ? mx__djgpp_vesa_linear15 : mx__djgpp_vesa_banked15;

			/* For 565 modes */
		} else if ((mode.RedMaskSize == 5) && (mode.RedMaskPos == 11)
				   && (mode.GreenMaskSize == 6) && (mode.GreenMaskPos == 5)
				   && (mode.BlueMaskSize == 5) && (mode.BlueMaskPos == 0)) {
			mx__djgpp_vesa_pixelline = mx__djgpp_vesa_linearmode ? mx__djgpp_vesa_linear16 : mx__djgpp_vesa_banked16;

			/* We dont recognize the 16 bit color mode */
		} else
			return false;

		/* For 24 bit modes */
	} else if (mode.BitsPerPixel == 24) {

		if ((mode.RedMaskSize == 8) && (mode.RedMaskPos == 16)
			&& (mode.GreenMaskSize == 8) && (mode.GreenMaskPos == 8)
			&& (mode.BlueMaskSize == 8) && (mode.BlueMaskPos == 0)) {
			mx__djgpp_vesa_pixelline = mx__djgpp_vesa_linearmode ? mx__djgpp_vesa_linear24 : mx__djgpp_vesa_banked24;

			/* We dont recognize the 24 bit color mode */
		} else
			return false;

		/* For 32 bit modes */
	} else if (mode.BitsPerPixel == 32) {

		if ((mode.RedMaskSize == 8) && (mode.RedMaskPos == 16)
			&& (mode.GreenMaskSize == 8) && (mode.GreenMaskPos == 8)
			&& (mode.BlueMaskSize == 8) && (mode.BlueMaskPos == 0)) {
			mx__djgpp_vesa_pixelline = mx__djgpp_vesa_linearmode ? mx__djgpp_vesa_linear32 : mx__djgpp_vesa_banked32;

			/* We dont recognize the 32 bit color mode */
		} else
			return false;

		/* We dont recognize the graphics mode */
	} else
		return false;

	/* Reset the mouse driver */
	r.x.ax = 0x0000;
	__dpmi_int(0x33, &r);
	if (r.x.ax)
		mx__djgpp_mouse_valid = true;
	else
		mx__djgpp_mouse_valid = false;

	/* Inform the user of the results */
	args->w = mode.XResolution;
	args->h = mode.YResolution;
	args->c = mode.BitsPerPixel;
	args->title = "djgpp_vesa";
	args->pointer = false;

	mx_drs_area(args->w - 1, args->h - 1);

	return true;
}

static void mx__djgpp_vesa_stop(void)
{
	__dpmi_regs reg;

	mx__djgpp_vesa_bank_switch(0);

	/* Release pm mode bank switcher */
	/*    __unlock_dpmi_data(mx__djgpp_vesa_pm_info, mx__djgpp_vesa_pm_info_size); */
	if (mx__djgpp_vesa_pm_info)
		free(mx__djgpp_vesa_pm_info);
	mx__djgpp_vesa_pm_info = 0;
	mx__djgpp_vesa_pm_bank_switcher = 0;

	/* Release physical mapping for linear mode */
	if (mx__djgpp_vesa_linearmode) {
		__dpmi_free_physical_address_mapping(&mx__djgpp_vesa_meminfo);
		__dpmi_free_ldt_descriptor(mx__djgpp_vesa_video_ds);
		mx__djgpp_vesa_linearmode = false;
	}

	/* set a text mode might help problems going vesa->vga */
	/*      mx__djgpp_vesa_try(0x108); */

	reg.x.ax = 0x0003;
	__dpmi_int(0x10, &reg);

	textmode(C80);
}

static void mx__djgpp_vesa__flush(const MX_RECT * rect, MX_PIXEL * array, const int stride)
{
	int y = rect->y1;

	/* Set the selector for fast access to the screen */
	if (mx__djgpp_vesa_linearmode)
		_farsetsel(mx__djgpp_vesa_video_ds);
	else
		_farsetsel(_dos_ds);

	/* Push the buffer to the visible screen */
	while (y <= rect->y2) {
		mx__djgpp_vesa_pixelline(rect->x1, y, rect->x2, array);
		array += stride;
		++y;
	}
}

static void mx__djgpp_vesa_flush(MX_RECT * rect)
{
	mx__gfx_flush(mx__djgpp_vesa__flush, rect);
}

static unsigned mx__djgpp_vesa_poll(void)
{
	mx_drs_update(mx__djgpp_vesa_flush);
	return true;
}

static void mx__djgpp_vesa_dirty(const MX_RECT * rect)
{
	mx_drs_dirty(rect, true);
}

const MX_DRIVER mx__djgpp_vesa = {
	mx__djgpp_vesa_start,
	mx__djgpp_vesa_stop,

	mx__djgpp_vesa_poll,
	mx__djgpp_vesa_dirty,

	mx__djgpp_pointer,
	mx__djgpp_key
};

#endif
